#ifndef PCMPLAYER_H
#define PCMPLAYER_H

#define ALSA_PCM_NEW_HW_PARAMS_API
#include <alsa/asoundlib.h>
#include "MusicCodec.h"

class PCMPlayer
{
private:
	snd_pcm_t *Handle;
	snd_pcm_hw_params_t *DeviceParams;
	int Channels;
	int BitFormat;
	int DataBufferSize;
	int PCMBufferSize;
	char* PCMBuffer;
	char* DataBuffer;
	bool PerformADrop;
	pthread_mutex_t *BufferLoaderMutex;
	pthread_cond_t *BufferLoaderCond;
	long unsigned int FramesAmount;
	unsigned int SamplingFrequency;


	Error ChangeFrequency(unsigned int SamplingFrequency) {
		snd_pcm_hw_params_set_rate_near(Handle, DeviceParams, &SamplingFrequency, 0);
		int rc = snd_pcm_hw_params(Handle, DeviceParams);
		if (rc < 0)
			return Error("PCMPlayer.cpp", "Initialization error", std::string("Unable to set PCM hardware parameters: ") + std::string(snd_strerror(rc)) + ".");
		else
			return Error();
	}
	int ChooseFormat(int BitFormat) {
		switch (BitFormat) {
			case 8:
				if (!snd_pcm_hw_params_test_format(Handle, DeviceParams, SND_PCM_FORMAT_S8)) {
					snd_pcm_hw_params_set_format(Handle, DeviceParams, SND_PCM_FORMAT_S8);
					return 8;
				} else
					ChooseFormat(0);
			break;
			case 16:
				if (!snd_pcm_hw_params_test_format(Handle, DeviceParams, SND_PCM_FORMAT_S16_LE)) {
					snd_pcm_hw_params_set_format(Handle, DeviceParams, SND_PCM_FORMAT_S16_LE);
					return 16;
				} else
					ChooseFormat(8);
			break;
			case 24:
				if (!snd_pcm_hw_params_test_format(Handle, DeviceParams, SND_PCM_FORMAT_S24_LE)) {
					snd_pcm_hw_params_set_format(Handle, DeviceParams, SND_PCM_FORMAT_S24_LE);
					return 24;
				} else
					ChooseFormat(16);
			break;
			case 32:
				if (!snd_pcm_hw_params_test_format(Handle, DeviceParams, SND_PCM_FORMAT_S32_LE)) {
					snd_pcm_hw_params_set_format(Handle, DeviceParams, SND_PCM_FORMAT_S32_LE);
					return 32;
				} else
					ChooseFormat(24);
			break;
			default:
				throw Error("PCMPlayer.h", "PCM Initialization", std::string("Wrong bit format: ") + std::to_string(BitFormat));
			break;
		}
	}
public:
	PCMPlayer();
	void Play();
	Error Drop(){
	    PerformADrop = true;
        return Error();
	}
	~PCMPlayer() {
		snd_pcm_drain(Handle);
		snd_pcm_close(Handle);
		delete[] PCMBuffer;
	}
};

#endif // PCMPLAYER_H
